home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The 640 MEG Shareware Studio 2
/
The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO
/
basic
/
qbredir.zip
/
QBREDIR.LST
< prev
next >
Wrap
File List
|
1992-03-01
|
37KB
|
501 lines
Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 1
QBREDIR.ASM
1
2 ; Quick Basic Redirected File Display
3 ;
4 ;
5 ; ---- A modified version of the original PD program REDVIEW.ASM ----
6 ; by Alexandr Novy and Peter Horak of Prague, Czechoslovakia
7 ;
8 ;
9 ; ---- This Public Domain Adaptation for QuickBasic Written By:
10 ;
11 ; Peter R. Barnes
12 ; Brentwood, TN
13 ; March, 1992
14 ;
15 ;
16 ; ---- To assemble the object module with TASM, use the command:
17 ;
18 ; TASM QBREDIR ;I don't have MASM,
19 ; ;but I'm sure the
20 ; ;syntax is the same
21 ;
22 ;
23 ; ---- To use in a QuickBasic program, compile your program
24 ; and link the module to your code, for example:
25 ;
26 ; BC /options YOURPROG; ;any options you want
27 ;
28 ; LINK /linkoptions YOURPROG + QBREDIR;
29 ;
30 ;
31 ;
32 ; ---- To call the routines in your program, use the following:
33 ;
34 ; CALL QFRedSet (FlagIntegerVariable%)
35 ;
36 ; CALL QFRedOff
37 ;
38 ;
39 ; --- IMPORTANT: READ THE QBREDIR.DOC FILE TO SEE HOW TO
40 ; PROPERLY USE THESE ROUTINES!! THE ROUTINES
41 ; INSTALL A CUSTOM INTERRUPT HANDLER WHICH
42 ; MUST BE TREATED WITH CARE!!
43 ;
44 ; ---- NOTE: CANNOT BE USED IN THE QB ENVIRONMENT (SEE
45 ; QBREDIR.DOC)
46 ;
47 ;
48 ; ---- I have added some comments to help explain the code as
49 ; it applies to QuickBasic, just for general (and my own)
50 ; reference. Some of the stuff is pretty elementary, so if
51 ; you are an assembly language wiz, just skip it; it's there
52 ; for the rest of us.
53 ;
54 ;
55 ;********************************************************************
56 ;********************************************************************
57 ;
58 ; Make our procedure names available to external programs
59 ;
Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 2
QBREDIR.ASM
60 public QFRedSet, QFRedOff
61 ;
62 ;
63 ;********************************************************************
64 ;
65 ; All Basic modules are assembled using the Medium
66 ; memory model, so we must add the proper assembler directives
67 ;
68 ;
69 0000 .MODEL Medium, Basic ;always, for QuickBasic
70 ;
71 0000 .CODE ;make the code segment our default
72 ;
73 ;
74 ;
75 ; We will let QBasic and TASM set the type of procedure, and we will
76 ; declare the module using the TASM simplified format
77 ;
78 ;
79 0000 QFRedSet proc uses si di es ds
80 = 0002 ARG flag:PTR = ARGLEN
81 ;
82 ;
83 ; That tells TASM to add code for a Basic procedure call, save
84 ; si,di,es,ds pointers, and that we are passing to the procedure a
85 ; two-byte pointer to the INTEGER (notice that I capitalized that --
86 ; three guesses why) variable /flag/, consisting of ARGLEN number of
87 ; bytes -- the QuickBasic 'call' automatically pushes that address onto
88 ; the stack and TASM calculates the correct offset to those stack
89 ; bytes when it assembles the code. All parameter addresses passed by
90 ; QuickBasic are near references (offset only, 1 word or 2 bytes)
91 ; unless you use the keyword SEG for the passed variable, which tells
92 ; QuickBasic to pass the address as a far reference (segment:offset,
93 ; 2 words or 4 bytes).
94 ; ---------
95 ; TASM knows from our 'Basic' directive that the variable we pass will
96 ; be a 2-byte address, and that Basic always makes far calls, leaving a full,
97 ; 2-word return address as the last 4 bytes on the stack; the variable
98 ; address, next on the stack, will be at [B(ase)P(ointer)+6] from the
99 ; present BP value (+ because the stack grows downward in memory).
100 ; That's also how TASM knows that we need a far return, not a near
101 ; return, and also how many bytes to discard, after it pops that QB
102 ; return address, to restore the stack after the procedure ends --
103 ; i.e. 2 bytes for the variable near address...
104 ;
105 ; push bp ;TASM automatically
106 ; mov bp,sp ;adds the code to save the B(ase)
107 ; ;P(ointer) to the stack in the stack
108 ; ;segment, and set up a new stack
109 ; ;that starts at the end of the old
110 ; ;one pointed to by the current
111 ; ;S(tack)P(ointer) -- i.e.
112 ; New bp = Old bp + sp
113 ; ;Actually, our new stack will begin
114 ; ;at New bp + sp, but this will keep
115 ; ;us away from the old stack for
116 ; ;all values of sp, in case sp gets
117 ; ;set to a lower value -- we will
118 ; ;never get lower than the new bp.
Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 3
QBREDIR.ASM
119 ; ;Of course, we have to be careful
120 ; ;to preserve that sp value in our
121 ; ;code by always 'popping' the same
122 ; ;number of bytes that we 'push'
123 ; ;onto the stack.
124 ;
125 ;
126 ; ;This is the code that is added
127 ; ;by TASM when it sees the 'Basic'
128 ; push si ;and the 'uses' directives, so we
129 ; push di ;don't need the explicit statements.
130 ; push es ;The address of /flag/ is placed on
131 ; push ds ;the stack first, and ARGLEN tells
132 ; ;TASM how many bytes to discard, when
133 ; ;we return from the call, to get
134 ; ;back to the original stack before
135 ; ;the call.
136 ;
137 ;
138 ;********************************************************************
139 ;
140 ;
141 ;
142 ; The first thing we do is grab and save the address of the QB variable
143 ; that was passed to us, on the stack, by QuickBasic when it made the
144 ; call; remember that TASM knows that [flag] means [BP+6]. In this
145 ; call, we passed the 2-byte offset address of the variable in the
146 ; ds data segment, so the 'word ptr [flag]' will have the 2-byte word
147 ; for the offset moved into ax; then the 2-byte word for the ds data
148 ; segment, whatever it is, will be stored. Both of those values are
149 ; copied into /flagadr/ by the following 3 mov instructions:
150 ;
151 ;
152 0000 55 8B EC 56 57 06 1E + mov ax,word ptr [flag] ;save the offset of passed
153 8B 46 06
154 000A 2E: A3 00EDr mov cs:flagadr,ax ;integer variable /flag/ in
155 ; ;memory location /flagadr/
156 ;
157 ;
158 ; That saves the offset that we passed, now we save the data segment
159 ; pointer that is in ds at the time of the call. Since QuickBasic
160 ; only has one data segment for integer variables, ds will already be
161 ; pointing to the segment containing our /flag/ variable.
162 ;
163 ; We could pass the segment pointer when we made the call from
164 ; QuickBasic by preceding the variable name with the SEG keyword; in
165 ; that situation, QuickBasic would push the full 4-byte address onto
166 ; the stack. We could also use the CALLS statement to call this
167 ; routine, instead of CALL, because that statement always passes the
168 ; full address of parameters. But neither of those actions are
169 ; required, because QuickBasic only has one data segment
170 ;
171 ; The following mov instruction saves our data segment for DOS to
172 ; use to find our variable when it executes the interrupt routine:
173 ;
174 ;
175 000E 2E: 8C 1E 00EFr mov cs:flagadr+2,ds ;save the segment pointer
176 ; ;of passed integer variable
177 ; ;/flag/ in second word
Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 4
QBREDIR.ASM
178 ; ;of /flagadr/ --
179 ; ;that's what DOS needs when
180 ; ;the interrupt is called
181 ;
182 ;
183 ; Next step is flagcheck to prevent multiple calls to this routine
184 ; if it has not been reset. Without this check, a second call would
185 ; save our new handler address as the original.
186 ;
187 ; Check to see if we have already installed the vector to the
188 ; interrupt handler routine. If we have, we don't want to do it
189 ; again, because we will lose the original vector
190 ;
191 0013 2E: A0 00ECr mov al,cs:[IntSetFlag] ;get flag to see if handler
192 0017 3C 00 cmp al,0 ;is already installed
193 0019 75 1F jne EndSet ;if set, we have installed
194 ; ;so go to exit
195 ;
196 001B B0 01 mov al, 1 ;else set the flag
197 001D 2E: A2 00ECr mov cs:[IntSetFlag],al
198 ;
199 ;
200 ; ;pick up original vector contents
201 ;
202 0021 B8 3521 mov ax,3521H ;for interrupt 21H (MS-DOS Services
203 0024 CD 21 int 21H ;Function Request handler)
204 ;
205 0026 2E: 89 1E 00F1r mov cs:Old21Offset,bx ;save the original vector
206 002B 2E: 8C 06 00F3r mov cs:Old21Segment,es ;in our data area
207 ;
208 ;
209 ; ;a call to DOS services INT21H for
210 ; ;the SET INTERRUPT function 25xxH
211 ; ;requires ds:dx to be set to the
212 ; ;interrupt address, so the next two
213 ; ;statements set the ds segment
214 ; ;equal to the cs segment, where
215 ; ;our new handler procedure is
216 ; ;located
217 ;
218 ;
219 0030 0E push cs ;push our code segment onto stack
220 0031 1F pop ds ;pop it back off to set ds
221 ; ;i.e. sets ds=cs
222 ;
223 0032 BA 0042r mov dx,offset QDualDisp ;load offset of our Int 21 handler
224 0035 B8 2521 mov ax,02521H ;and reset vector to point to
225 0038 CD 21 int 21H ;our procedure
226 ;
227 ;
228 ; pop ds ;restore registers and
229 ; pop es ;return to our QB program
230 ; pop di
231 ; pop si ;NOTE--TASM automatically adds
232 ; pop bp ;the code to restore registers
233 ;
234 003A 1F 07 5F 5E 5D CA + EndSet: ret
235 0002
236 ;
Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 5
QBREDIR.ASM
237 ; actually, it's 'ret [ARGLEN]' ;TASM automatically makes the
238 ; ;return discard the /flag/ address
239 ; ;bytes from the stack--the number
240 ; ;of bytes to discard was set by
241 ; ;the ARGLEN directive
242 ;
243
244 ;
245 ;
246 ; Following is our new interrupt handler which is called by
247 ; MS-DOS whenever an Interrupt 21 MS-DOS Services Request
248 ; is detected. The procedure first checks our enable flag
249 ; to determine if we want the dual output activated; the address of
250 ; this flag was passed by the QB program during the call to
251 ; the QFRedSet function. If the flag is not set, we just skip our
252 ; handler and proceed to the regular interrupt 21 routine.
253 ;
254 ; The handler then checks to see what type of service is being requested.
255 ; If it is a call to the DOS routines to output a character or a string
256 ; to STDOUT, the standard DOS output device, which is redirectible and
257 ; is always designated with file handle 1, then our procedure essentially
258 ; duplicates the routine by sending the output to STDERR, the standard
259 ; DOS error output device, which is always the display, file handle 2,
260 ; and whose output cannot be redirected. Then the handler exits to the
261 ; normal Interrupt 21 procedure to let it process the original call.
262 ;
263 ; Note that the routine is part of the QFRedSet procedure, but it is never
264 ; executed when that procedure is called by the QB program, because that
265 ; procedure ends at the 'ret' opcode at EndSet, above.
266 ;
267 QDualDisp:
268
269 0042 9C pushf ;save everything we can --
270 0043 50 push ax ;i.e. all registers and flags
271 0044 53 push bx
272 0045 51 push cx ;note that we save the flags
273 0046 52 push dx ;even though DOS did the same
274 0047 55 push bp ;thing when it called the
275 0048 56 push si ;interrupt routine
276 0049 57 push di
277
278
279 004A 53 push bx ;save entering register values
280 004B 1E push ds
281 004C 2E: 8B 1E 00EDr mov bx,cs:flagadr ;get address of flag value passed
282 0051 2E: 8E 1E 00EFr mov ds,cs:flagadr+2 ;from our QB program
283 0056 8A 07 mov al,ds:bx ;get flag value into al
284 0058 1F pop ds ;retrieve register values
285 0059 5B pop bx
286
287 005A 0A C0 or al, al ;see if value was zero
288 005C 74 57 je EndOur21 ;yes, don't do anything, go
289 ;to the regular interrupt 21 routine
290
291 005E 80 FC 02 cmp ah,02 ;was it a call to write character to STDOUT
292 0061 75 1C jne Int9Chk ;no, jump to next check
293
294 0063 1E push ds ;yes, save entering data segment value
295 0064 0E push cs ;set ds=cs
Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 6
QBREDIR.ASM
296 0065 1F pop ds
297 0066 2E: 88 16 00F5r mov cs:character,dl ;save the character to write in character
298 006B B4 40 mov ah,40h ;set up for int 21 function 40h
299 ;to write character to output device
300 006D BB 0002 mov bx,2 ;set file handle=2 for STDERR
301 0070 BA 00F5r mov dx,OFFSET character ;get the pointer to the character
302 0073 B9 0001 mov cx,1 ;one character to be written
303 0076 9C pushf ;save current flag status
304 0077 2E: FF 1E 00F1r call dword ptr cs:[Old21];use old interrupt routine to output
305 ;the character to the display,
306 ;it will pop the flags saved on stack
307 ;when it returns, because it ends with
308 ;iret, since it is an interrupt routine
309 007C 1F pop ds ;retrieve our character
310 007D EB 36 jmp short EndOur21 ;exit to old routine to write the character
311 ;to redirected output
312
313 Int9Chk:
314 007F 80 FC 09 cmp ah,09 ;was it a call to write string to output
315 0082 75 1C jne Int40Chk ;no, go to next check
316
317 Chrloop:
318 0084 8B F2 mov si,dx ;yes, set index
319 0086 80 3C 24 cmp byte ptr ds:[si],'$';are we at the end of the string
320 0089 74 13 je End2109 ;yes, go to finish
321 008B B4 40 mov ah,40h ;no, set up for int 21 function 40h
322 008D BB 0002 mov bx,2 ;output to STDERR
323 0090 B9 0001 mov cx,1
324 0093 52 push dx
325 0094 9C pushf
326 0095 2E: FF 1E 00F1r call dword ptr cs:[Old21];write the character as above
327 009A 5A pop dx
328 009B 42 inc dx ;bump index to next character
329 009C EB E6 jmp short Chrloop ;loop until done
330 End2109:
331 009E EB 15 jmp short EndOur21 ;exit to old routine to write the character
332 ;to redirected output
333
334 Int40Chk:
335 00A0 80 FC 40 cmp ah,40h ;was it a call to write to file or device
336 00A3 75 10 jne EndOur21 ;no, done, exit to regular int 21
337
338 00A5 83 FB 01 cmp bx,1 ;yes, then was it a call to STDOUT
339 00A8 75 0B jne EndOur21 ;no, done, exit
340
341 00AA 1E push ds ;yes, save character
342 00AB BB 0002 mov bx,2 ;set file handle for STDERR
343 00AE 9C pushf
344 00AF 2E: FF 1E 00F1r call dword ptr cs:[Old21] ;use old interrupt routine to output
345 00B4 1F pop ds ;retrieve character
346
347 EndOur21:
348
349 ;retrieve registers
350 00B5 5F pop di ;this resets registers
351 00B6 5E pop si ;and flags to exactly
352 00B7 5D pop bp ;where they were when we
353 00B8 5A pop dx ;entered our custom handler
354 00B9 59 pop cx
Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 7
QBREDIR.ASM
355 00BA 5B pop bx
356 00BB 58 pop ax
357 00BC 9D popf
358 ;
359 ;
360 00BD 2E: FF 2E 00F1r jmp dword ptr cs:[Old21] ;go to regular int 21 routine
361 ;
362 ;
363 ;
364 ;
365 00C2 QFRedSet endp
366 ;
367 ;
368 ;
369 ; The function QFRedOff is called by the QB program to
370 ; return the MS-DOS interrupt vector 21H to its' original state.
371 ; The function is used in the form:
372 ;
373 ; QFRedOff()
374 ;
375 ;
376 00C2 QFRedOff proc uses si di es ds ;Restore MS-DOS Services
377 ; ;interrupt vector 21H
378 ; push bp ;to its' original state
379 ; mov bp,sp
380 ; push ds ;same code added as above
381 ; etc.
382 ;
383 ;
384 ; First we must check our IntSetFlag to see if the
385 ; vectors to our handler were installed; if not, we
386 ; do not want to reset them here, either
387 ;
388 ;
389 00C2 55 8B EC 56 57 06 1E + mov al,cs:[IntSetFlag] ;Get our inhibit flag
390 2E: A0 00ECr
391 00CD 3C 01 cmp al,1 ;see if it is set
392 00CF 75 15 jne EndOff ;no, go to exit
393 ;else, reset vector
394 ;
395 ;
396 00D1 2E: 8B 16 00F1r mov dx,cs:Old21Offset ;Set interrupt 21H MS-DOS
397 00D6 2E: 8E 1E 00F3r mov ds,cs:Old21Segment ;Services Request
398 00DB B8 2521 mov ax,02521H ;back to its' original vector
399 00DE CD 21 int 21H
400 ;
401 ;
402 00E0 B0 00 mov al, 0 ;reset the flag to enable
403 00E2 2E: A2 00ECr mov cs:[IntSetFlag],al ;the QFRedSet routine
404 ;
405 ; pop ds ;restore registers and
406 ; pop bp ;return to QB program --
407 ; etc. ;same exit code added as above
408 ;
409 00E6 1F 07 5F 5E 5D CB EndOff: ret
410 ;
411 00EC QFRedOff endp
412 ;
413 ;
Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 8
QBREDIR.ASM
414 ;
415 00EC 00 IntSetFlag db 0 ;this flag inhibits QFRedSet operation
416 ; ;after it has redirected vector
417 ; ;until QFRedOff resets it, and does
418 ; ;the same for QFRedOff if QFRedSet
419 ; ;has not redirected vector
420 ;
421 00ED 0000 0000 flagadr dw 0,0 ;Long address of QB program's
422 ; ;display redirected output flag --
423 ; ;note that each '0' is actually 2 bytes
424 ;
425 Old21 Label Dword
426 00F1 ???? Old21Offset dw ? ;Original contents of MS-DOS
427 00F3 ???? Old21Segment dw ? ;Interrupt 21H vector
428
429 ;
430 ;
431 00F5 ?? character DB ? ;storage for output character
432 ;
433
434 end
Turbo Assembler Version 1.0 03-01-92 18:14:09 Page 9
Symbol Table
Symbol Name Type Value Cref defined at #
??DATE Text "03-01-92"
??FILENAME Text "QBREDIR "
??TIME Text "18:14:09"
??VERSION Number 0100
@CODE Text QBREDIR_TEXT #69 #71
@CODESIZE Text 1 #69
@CPU Text 0101H
@CURSEG Text QBREDIR_TEXT #71
@DATA Text DGROUP #69
@DATASIZE Text 0 #69
@FILENAME Text QBREDIR
@WORDSIZE Text 2 #71
ARGLEN Number 0002 #80
CHARACTER Byte QBREDIR_TEXT:00F5 297 301 #431
CHRLOOP Near QBREDIR_TEXT:0084 #317 329
END2109 Near QBREDIR_TEXT:009E 320 #330
ENDOFF Near QBREDIR_TEXT:00E6 392 #409
ENDOUR21 Near QBREDIR_TEXT:00B5 288 310 331 336 339 #347
ENDSET Near QBREDIR_TEXT:003A 193 #234
FLAG Number [DGROUP:BP+0006] #80 152
FLAGADR Word QBREDIR_TEXT:00ED 154 175 281 282 #421
INT40CHK Near QBREDIR_TEXT:00A0 315 #334
INT9CHK Near QBREDIR_TEXT:007F 292 #313
INTSETFLAG Byte QBREDIR_TEXT:00EC 191 197 389 403 #415
OLD21 Dword QBREDIR_TEXT:00F1 304 326 344 360 #425
OLD21OFFSET Word QBREDIR_TEXT:00F1 205 396 #426
OLD21SEGMENT Word QBREDIR_TEXT:00F3 206 397 #427
QDUALDISP Near QBREDIR_TEXT:0042 223 #267
QFREDOFF Far QBREDIR_TEXT:00C2 60 #376
QFREDSET Far QBREDIR_TEXT:0000 60 #79
Groups & Segments Bit Size Align Combine Class Cref defined at #
DGROUP Group #69 69
_DATA 16 0000 Word Public DATA #69
QBREDIR_TEXT 16 00F6 Word Public CODE #69 69 #71 71